package org.wavetech.adapter;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.HashMap;
import java.util.List;

public class FastAdapter<DATA> extends RecyclerView.Adapter<ViewHolder> implements HeaderViewOwner, FooterViewOwner {

    public static final int ITEM_HEADER = -9000;
    public static final int ITEM_FOOTER = -9001;

    protected final Context context;
    protected int layoutRes = -1;
    protected final List<DATA> data;

    protected boolean headerAsFlow;
    protected boolean footerAsFlow;
    private final HeaderModule headerModule;
    private final FooterModule footerModule;

    private OnItemClickListener<DATA> onItemClickListener;
    private final HashMap<Integer, OnItemChildClickListener<DATA>> onItemChildClickListeners;

    private ItemAnimator itemAnimator;
    private boolean animateOnce;
    private int lastAnimationPos = -1;

    private SpanSizeLookup<DATA> spanSizeLookup;

    public FastAdapter(@NonNull Context context, List<DATA> data) {
        this(context, -1, data);
    }

    public FastAdapter(@NonNull Context context, @LayoutRes int layoutRes, List<DATA> data) {
        this.context = context;
        this.layoutRes = layoutRes;
        this.data = data;

        this.headerModule = new HeaderModule(this.context, this);
        this.footerModule = new FooterModule(this.context, this);

        this.spanSizeLookup = new SpanSizeLookup<>();
        this.onItemChildClickListeners = new HashMap<>();
    }

    @NonNull
    @Override
    @NoOverride
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        switch (viewType) {
            case ITEM_HEADER:
                return new ViewHolder(headerModule.getContainerView());
            case ITEM_FOOTER:
                return new ViewHolder(footerModule.getContainerView());
        }
        ViewHolder holder = onCreateViewHolderImpl(parent, viewType);
        bindViewHolderListener(holder);
        return holder;
    }

    /**
     * 重写此方法返回自定义 ViewHolder, 同时也需要重写 {@link #getItemViewTypeImpl(int, int)} 方法
     *
     * @param parent
     * @param viewType
     * @return
     */
    protected ViewHolder onCreateViewHolderImpl(@NonNull ViewGroup parent, int viewType) {
        if (layoutRes <= 0) {
            throw new IllegalStateException("You have not set the layoutRes or override this method.");
        }
        return new ViewHolder(parent, layoutRes);
    }

    @Override
    @NoOverride
    public int getItemViewType(int position) {
        boolean isFirstOne = position == 0;
        boolean isLastOne = position == (getItemCount() - 1);
        boolean hasHeader = headerModule.hasHeaderView();
        if (isFirstOne && hasHeader) {
            return ITEM_HEADER;
        }
        if (isLastOne && footerModule.hasFooterView()) {
            return ITEM_FOOTER;
        }
        int dataPosition = hasHeader ? (position - 1) : position;
        return getItemViewTypeImpl(position, dataPosition);
    }

    /**
     * 重写此方法返回自定义 itemType
     *
     * @param position
     * @param dataPosition
     * @return
     */
    protected int getItemViewTypeImpl(int position, int dataPosition) {
        return super.getItemViewType(position);
    }

    private void bindViewHolderListener(ViewHolder holder) {
        holder.itemView.setOnClickListener(v -> {
            if (onItemClickListener == null) return;
            int position = holder.getAdapterPosition();
            if (position == RecyclerView.NO_POSITION) return;
            DATA d = data.get(position - (headerModule.hasHeaderView() ? 1 : 0));
            onItemClickListener.onItemClick(v, holder, d);
        });
        for (Integer id : onItemChildClickListeners.keySet()) {
            if (id == null) continue;
            View targetView = holder.findViewById(id);
            if (targetView == null) continue;
            targetView.setOnClickListener(v -> {
                int position = holder.getAdapterPosition();
                if (position == RecyclerView.NO_POSITION) return;
                DATA d = data.get(position - (headerModule.hasHeaderView() ? 1 : 0));
                OnItemChildClickListener<DATA> listener = onItemChildClickListeners.get(id);
                if (listener == null) return;
                listener.onItemClick(v, holder, d);
            });
        }
    }

    public void setOnItemClickListener(OnItemClickListener<DATA> listener) {
        this.onItemClickListener = listener;
    }

    public void setOnItemChildClickListener(@IdRes int id, OnItemChildClickListener<DATA> listener) {
        this.onItemChildClickListeners.put(id, listener);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        switch (getItemViewType(position)) {
            case ITEM_HEADER:
            case ITEM_FOOTER:
                break;
            default:
                int dataPosition = position - (headerModule.hasHeaderView() ? 1 : 0);
                bindData(holder, dataPosition, data.get(dataPosition));
        }
    }

    /**
     * 重写此方法进行视图数据绑定
     *
     * @param holder
     * @param data
     */
    public void bindData(ViewHolder holder, int dataPosition, DATA data) {
    }

    @Override
    public int getItemCount() {
        int extra = 0;
        if (headerModule.hasHeaderView()) {
            extra++;
        }
        if (footerModule.hasFooterView()) {
            extra++;
        }
        return data.size() + extra;
    }

    @Override
    public void addHeaderView(View view) {
        headerModule.addHeaderView(view);
    }

    @Override
    public void removeHeaderView(View view) {
        headerModule.removeHeaderView(view);
    }

    @Override
    public void removeHeaderView(int position) {
        headerModule.removeHeaderView(position);
    }

    @Override
    public void addFooterView(View view) {
        footerModule.addFooterView(view);
    }

    @Override
    public void removeFooterView(View view) {
        footerModule.removeFooterView(view);
    }

    @Override
    public void removeFooterView(int position) {
        footerModule.removeFooterView(position);
    }

    public void setSpanSizeLookup(SpanSizeLookup<DATA> spanSizeLookup) {
        this.spanSizeLookup = spanSizeLookup;
        notifyDataSetChanged();
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            GridLayoutManager layoutManager = (GridLayoutManager) manager;
            layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    int itemViewType = getItemViewType(position);
                    int spanCount = layoutManager.getSpanCount();
                    DATA d = null;
                    if (itemViewType != ITEM_HEADER && itemViewType != ITEM_FOOTER) {
                        d = data.get(position - (headerModule.hasHeaderView() ? 1 : 0));
                    }
                    return spanSizeLookup.getSpanSize(itemViewType, position, d, spanCount, headerAsFlow, footerAsFlow);
                }
            });
        }
    }

    @Override
    public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        startItemAnimation(holder);
    }

    private void startItemAnimation(@NonNull ViewHolder holder) {
        if (itemAnimator == null) return;
        int position = holder.getLayoutPosition();
        if (animateOnce && position <= lastAnimationPos) return;
        boolean enter = position > lastAnimationPos;
        itemAnimator.animate(holder.itemView, enter);
        lastAnimationPos = position;
    }

    public void setItemAnimator(ItemAnimator animator, boolean once) {
        this.itemAnimator = animator;
        this.animateOnce = once;
    }
}
